package com.dr.springboot.starter.aspect;

import com.dr.springboot.starter.exception.RestException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

@Aspect
@Component
@Slf4j
public class TestAspect {


    //    todo aspect 创建一个切面点
    @Pointcut("execution(* com.dr.springboot.starter.controller.AOPController.*(..))")
    public void executePointcut() {
    }

    @Before("executePointcut()")
    public String beforePointcut(JoinPoint joinPoint) {
        log.info("aspect：前置消息");
        Signature signature = joinPoint.getSignature();
        log.info("aspect：前置消息:" + signature.getDeclaringTypeName() + ":" + signature.getName());
        Object[] args = joinPoint.getArgs();
        for (Object obj : args) {
            log.info("aspect：前置消息:" + (String) obj);
        }

        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        Cookie[] cookies = request.getCookies();
        for (Cookie cookie : cookies) {
            log.info("aspect：前置消息:" + cookie.getName() + "::" + cookie.getValue());
        }
        return "前置";
    }

    //    todo aspect returning的值必须he参数匹配
    @AfterReturning(value = "executePointcut()", returning = "obj")
    public void returnPointcut(JoinPoint joinPoint, Object obj) {
//       todo aspect AfterReturning中改变返回值后不会有任何效果
        log.info("aspect:后置返回通知(AfterReturning):" + obj.toString());
    }

    @AfterThrowing(value = "executePointcut()", throwing = "exception")
//    todo aspect 异常捕获类型必须he抛出的类型一致
    public void throwException(JoinPoint joinPoint, RestException exception) {
        log.info("aspect:后置异常通知(AfterThrowing): exception:{}", exception.getMessage());
    }

    @After(value = "executePointcut()")
    public void after(JoinPoint joinPoint) {
        log.info("aspect:后置通知执行了(After)");
    }

//    todo aspect 可以决定入参值、返回值、是否执行,他可以介入到切面运行的各个周期，对值进行影像
    @Around(value = "executePointcut()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("aspect:环绕通知的目标方法名（around）：" + proceedingJoinPoint.getSignature().getName());
        try {
            Object obj = proceedingJoinPoint.proceed();
            log.info("aspect:环绕通知（around）:"+obj);
            return obj+"!!!!!!";
        } catch (Throwable throwable) {
            throw throwable;
        }
    }

//    todo aspect 时序：around（执行方法前）=》before=》around（执行方法后）=》after=》afterReturning/afterThrowing



    /*@Around("execution(* com.dr.springboot.starter.controller.AOPController.*(..))")
    public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("time aspect start");
        long start = new Date().getTime();

        // 获取methodInvoke
        Field proxy = pjp.getClass().getDeclaredField("pname");
        proxy.setAccessible(true);
        ReflectiveMethodInvocation methodInvoke = (ReflectiveMethodInvocation) proxy.get(pjp);

        // 获取增强的类
        String controller = methodInvoke.getMethod().getDeclaringClass().getName();
        // 获取增强的方法名
        String method = methodInvoke.getMethod().getName();
        // 获取增强方法的参数
        Object[] args = methodInvoke.getArguments();
//        Object[] args = pjp.getArgs();

        System.out.println("增强   的    类:"+ controller);
        System.out.println("增强   的  方法:"+ method);
        for (Object arg : args) {
            System.out.println("增强的方法的参数:"+ arg);
        }


        Object object = pjp.proceed();

        System.out.println("time aspect 耗时:"+ (new Date().getTime() - start));

        System.out.println("time aspect end");

        return object;
    }*/

}
